package com.ms.service.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ms.constant.GenderTypeConstant;
import com.ms.constant.MessageConstant;
import com.ms.constant.PasswordConstant;
import com.ms.context.BaseContext;
import com.ms.domain.dto.ChangePasswordDTO;
import com.ms.domain.dto.ForgetPasswordDTO;
import com.ms.domain.dto.UserLoginDTO;
import com.ms.domain.dto.UserProfileUpdateDTO;
import com.ms.domain.entity.User;
import com.ms.domain.vo.UserVO;
import com.ms.exception.AccountNotFoundException;
import com.ms.exception.BaseException;
import com.ms.mapper.UserMapper;
import com.ms.service.UserService;
import com.ms.utiles.OssUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserMapper userMapper;
    private final OssUtil ossUtil;


    /**
     * 用户登录
     *
     * @param userLoginDTO 用户登录数据
     * @return 用户信息
     */
    @Override
    public User login(UserLoginDTO userLoginDTO) {
        String username = userLoginDTO.getUsername();
        String password = userLoginDTO.getPassword();

        // 根据用户名查询用户
        User user = userMapper.selectOne(
                Wrappers.<User>lambdaQuery()
                        .eq(User::getUsername, username)
        );

        // 检查用户是否存在且密码匹配
        if (user == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        // 密码匹配
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(user.getPassword())) {
            throw new AccountNotFoundException(MessageConstant.PASSWORD_ERROR);
        }

        return user;
    }

    /**
     * 用户注册
     *
     * @param userLoginDTO 用户注册数据
     */
    @Override
    @Transactional
    public void register(UserLoginDTO userLoginDTO) {
        User user = new User();
        BeanUtils.copyProperties(userLoginDTO, user);

        user.setNickname(user.getUsername());
        user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes()));

        userMapper.insert(user);
    }

    /**
     * 用户忘记密码
     *
     * @param changePasswordDTO 用户登录数据
     */
    @Override
    public void changePassword(ChangePasswordDTO changePasswordDTO) {
        String username = changePasswordDTO.getUsername();
        User user = userMapper.selectOne(Wrappers.<User>lambdaQuery()
                .eq(User::getUsername, username)
        );
        if (user == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        // 约定：忘记密码重置密码，DTO中只包含用户名、新密码，旧密码为空
        // 旧密码不为空，则需要验证旧密码
        if (changePasswordDTO.getOldPassword() != null) {
            // 旧密码和登录密码不匹配
            if (!(DigestUtils.md5DigestAsHex(changePasswordDTO.getOldPassword().getBytes())).equals(user.getPassword())) {
                throw new AccountNotFoundException(MessageConstant.PASSWORD_ERROR);
            }
        }
        // 新密码不为空，则需要验证新密码
        if (changePasswordDTO.getNewPassword() == null) {
            throw new AccountNotFoundException(MessageConstant.USER_PASSWORD_EMPTY);
        }
        String newPassword = changePasswordDTO.getNewPassword();
        String password = DigestUtils.md5DigestAsHex(newPassword.getBytes());
        user.setPassword(password);
        userMapper.updateById(user);
        log.info("用户{}的密码已重置为{}", username, password);
    }

    /**
     * 根据昵称获取用户信息
     *
     * @param nickname 用户昵称
     */
    @Override
    @Cacheable(value = "users",
            key = "T(com.ms.constant.CacheConstant).USER_BY_NICKNAME + ':' + #nickname",
            unless = "#result == null")
    public User getUserInfoByNickName(String nickname) {
        User user = userMapper.selectOne(Wrappers.<User>lambdaQuery()
                .eq(User::getNickname, nickname)
        );
        if (user == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        user.setPassword(PasswordConstant.PASSWORD_NOT_DISPLAY);
        return user;
    }

    /**
     * 修改用户信息
     *
     * @param userProfileUpdateDTO 用户信息
     */
    @Override
    @Transactional
    @Caching(evict = {
            @CacheEvict(value = "users", key = "T(com.ms.constant.CacheConstant).USER_BY_NICKNAME + ':' + #userProfileUpdateDTO.nickname", beforeInvocation = true),
            @CacheEvict(value = "users", key = "T(com.ms.constant.CacheConstant).ALL_USERS", beforeInvocation = true)
    })
    public void updateUserInfo(UserProfileUpdateDTO userProfileUpdateDTO,
                               MultipartFile avatarFile,
                               MultipartFile backgroundFile) {
        Integer userId = BaseContext.getCurrentId();
        if (userId == null) {
            throw new AccountNotFoundException(MessageConstant.USER_GET_CURRENT_ID_FAILED);
        }

        User user = new User();
        BeanUtils.copyProperties(userProfileUpdateDTO, user);

        // 处理gender字段转换
        if (userProfileUpdateDTO.getGender() != null) {
            try {
                user.setGender(userProfileUpdateDTO.getGender());
            } catch (IllegalArgumentException e) {
                user.setGender(GenderTypeConstant.UNKNOWN);
            }
        }

        // 通过阿里云OSS上传文件
        log.info("用户{}上传头像文件{}", user.getUsername(), avatarFile);
        String image = ossUtil.uploadFile(avatarFile, "image");
        log.info("头像文件上传成功{}", image);
        log.info("用户{}上传背景文件{}", user.getUsername(), backgroundFile);
        String background = ossUtil.uploadFile(backgroundFile, "image");
        log.info("背景文件上传成功{}", background);
        user.setAvatarUrl(image);
        user.setBackgroundUrl(background);
        user.setId(userId); // 从上下文中获取用户ID，确保安全性

        userMapper.updateById(user);
        User updatedUser = userMapper.selectById(userId);
        if (updatedUser != null) {
            log.info("用户{}的资料已修改为{}", updatedUser.getUsername(), user);
        }
    }

    /**
     * 获取用户信息
     *
     * @param id 用户ID
     * @return 用户信息
     */
    @Override
    @Cacheable(value = "users",
            key = "T(com.ms.constant.CacheConstant).USER_BY_ID + ':' + #id",
            unless = "#result == null")
    public UserVO getUserInfoById(Integer id) {
        User user = userMapper.selectById(id);
        if (user == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    /**
     * 忘记密码重置密码
     *
     * @param forgetPasswordDTO 忘记密码信息
     */
    @Override
    @Transactional
    public void forgetPassword(ForgetPasswordDTO forgetPasswordDTO) {
        // 验证账号是否存在
        User user = userMapper.selectOne(Wrappers.<User>lambdaQuery()
                .eq(User::getUsername, forgetPasswordDTO.getUsername())
        );
        if (user == null) {
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }
        if (!user.getEmail().equals(forgetPasswordDTO.getEmail())) {
            throw new BaseException(MessageConstant.EMAIL_NOT_MATCH);
        }
        userMapper.update(User.builder()
                        .password((DigestUtils.md5DigestAsHex(forgetPasswordDTO.getNewPassword().getBytes())))
                        .build(),
                Wrappers.<User>lambdaQuery()
                        .eq(User::getUsername, forgetPasswordDTO.getUsername())
        );
        log.info("用户{}修改密码成功", forgetPasswordDTO.getUsername());
    }

    /**
     * 获取所有用户
     *
     * @return
     */
    @Override
    @Cacheable(value = "users",
            key = "T(com.ms.constant.CacheConstant).ALL_USERS",
            unless = "#result == null || #result.isEmpty()")
    public List<User> getAllUsers() {
        return userMapper.selectList(null);
    }
}